/* eslint-disable @typescript-eslint/no-explicit-any */
class Query {
  elements: HTMLElement[]

  constructor(
    selector: string | HTMLElement | NodeListOf<HTMLElement> | HTMLElement[],
    context: Document | HTMLElement = document,
  ) {
    if (typeof selector === 'string') {
      this.elements = Array.from(context.querySelectorAll(selector)) as HTMLElement[]
    } else if (selector instanceof HTMLElement) {
      this.elements = [selector]
    } else if (selector instanceof NodeList || Array.isArray(selector)) {
      this.elements = Array.from(selector) as HTMLElement[]
    } else {
      this.elements = []
    }
  }

  each(callback: (el: HTMLElement, index: number) => void): this {
    this.elements.forEach((el, i) => callback(el, i))
    return this
  }

  find(selector: string): Query {
    const found = this.elements.flatMap((el) => Array.from(el.querySelectorAll(selector)) as HTMLElement[])
    return new Query(found)
  }

  data(key: string, value: string): Query {
    const selector = `[data-${key}="${value}"]`
    return this.find(selector)
  }

  text(value?: string): string | this {
    if (value === undefined) {
      return this.elements.map((el) => el.textContent ?? '').join('')
    }
    this.each((el) => {
      el.textContent = value
    })
    return this
  }

  html(value?: string): string | this {
    if (value === undefined) {
      return this.elements.map((el) => el.innerHTML).join('')
    }
    this.each((el) => {
      el.innerHTML = value
    })
    return this
  }

  addClass(cls: string): this {
    this.each((el) => el.classList.add(cls))
    return this
  }

  removeClass(cls: string): this {
    this.each((el) => el.classList.remove(cls))
    return this
  }

  css(prop: keyof CSSStyleDeclaration, value: string): this {
    this.each((el) => {
      // 强转是因为 TS 不保证 prop 都是可写属性
      ;(el.style as unknown as any)[prop] = value
    })
    return this
  }
}

// 工厂函数
export function query(
  selector: string | HTMLElement | NodeListOf<HTMLElement> | HTMLElement[],
  context?: Document | HTMLElement,
): Query {
  return new Query(selector, context)
}
